/*
	* Created by Varun Tyagi
	*
	* This tutorial shows the current frame rate. In every game we need to calculate frame rate 
	* for some purpose or the other. For examples, we want to know at what frame our game is 
	* running or we want to know how much juice we can get out of SDL rendering. 
*/

//The headers
#include "SDL.h"
#include "SDL_image.h"
#include "SDL_ttf.h"
#include <string>
#include <sstream>

//Screen attributes
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;

//The surfaces
SDL_Surface *pBackground = NULL;
SDL_Surface *pScreen = NULL;
SDL_Surface *pMessage = NULL;

//The event structure
SDL_Event event;

//The font
TTF_Font *font = NULL;

//The color of the font
SDL_Color textColor = { 0xF0, 0xFF, 0xF0 };

/* Timer class. */
class Timer
{
private:

	/* The time when the timer was started. */
	int m_iStartTick;

	/* The tick stored when the timer was paused. */
	int m_iPausedTick;

	/* The timer status. */
	bool m_bPaused;
	bool m_bStarted;

public:

	Timer();

	/* The clock actions. */
	void start( void );
	void stop( void );
	void pause( void );
	void unpause( void );
	void reset( void );

	/* Get the timer's time. */
	int get_Ticks( void );

	/* Check the status of the timer. */
	bool has_started( void );
	bool is_paused( void );
};

/* Constructor. */
Timer::Timer()
{
	/* Initialize the variables. */
	m_iStartTick = 0;
	m_iPausedTick = 0;

	m_bPaused = false;
	m_bStarted = false;
}

void Timer::start( void )
{
	/* Unpause the timer. */
	m_bPaused = false;

	/* Start the timer. */
	m_bStarted = true;

	/* Get the current clock time. */
	m_iStartTick = SDL_GetTicks();	
}

void Timer::stop( void )
{
	/* Stop the timer. */
	m_bStarted = false;

	/* Unpause the timer. */
	m_bPaused = false;
}

void Timer::pause( void )
{
	/* If the timer is running and isn't paused already .*/
	if( (m_bStarted == true) && (m_bPaused == false) )
	{
		/* Stop the timer. */
		m_bPaused = true;

		/* Calculate the pause ticks. */
		m_iPausedTick = SDL_GetTicks() - m_iStartTick;
	}
}

void Timer::unpause( void )
{
	/* Check if the timer is paused. */
	if( true == m_bPaused )
	{
		/* Unpause the timer. */
		m_bPaused = false;

		/* Reset the starting tick. */
		m_iStartTick = SDL_GetTicks() - m_iPausedTick;

		/* Reset the pause tick. */
		m_iPausedTick = 0;
	}
}

void Timer::reset( void )
{
	m_iStartTick = 0;
	m_iPausedTick = 0;
}

int Timer::get_Ticks( void )
{
	/* Check if the timer is running. */
	if( true == m_bStarted )
	{
		/* If the timer is paused. */
		if( true == m_bPaused )
		{
			/* Return the number of ticks when the timer was paused. */
			return m_iPausedTick;
		}
		else
		{
			/* return the current time minus the start time. */
			return SDL_GetTicks() - m_iStartTick;
		}
	}

	/* Return 0 if the timer is not running. */
	return 0;
}

bool Timer::has_started( void )
{
	return m_bStarted;
}

bool Timer::is_paused( void )
{
	return m_bPaused;
}


SDL_Surface *load_image( std::string filename )
{
	//The image that's loaded
	SDL_Surface* loadedImage = NULL;

	//The optimized surface that will be used
	SDL_Surface* optimizedImage = NULL;

	//Load the image
	loadedImage = IMG_Load( filename.c_str() );

	//If the image loaded
	if( loadedImage != NULL )
	{
		//Create an optimized surface
		optimizedImage = SDL_DisplayFormat( loadedImage );

		//Free the old surface
		SDL_FreeSurface( loadedImage );

		//If the surface was optimized
		if( optimizedImage != NULL )
		{
			//Color key surface
			SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF ) );
		}
	}

	//Return the optimized surface
	return optimizedImage;
}

void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL )
{
	//Holds offsets
	SDL_Rect offset;

	//Get offsets
	offset.x = x;
	offset.y = y;

	//Blit
	SDL_BlitSurface( source, clip, destination, &offset );
}

bool init()
{
	//Initialize all SDL subsystems
	if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
	{
		return false;
	}

	//Set up the screen
	pScreen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

	//If there was an error in setting up the screen
	if( pScreen == NULL )
	{
		return false;
	}

	//Initialize SDL_ttf
	if( TTF_Init() == -1 )
	{
		return false;
	}

	//Set the window caption
	SDL_WM_SetCaption( "Frame Rate Test", NULL );

	//If everything initialized fine
	return true;
}

bool load_files()
{
	//Load the background image
	pBackground = load_image( "background.png" );

	//Open the font
	font = TTF_OpenFont( "lazy.ttf", 36 );

	//If there was a problem in loading the background
	if( pBackground == NULL )
	{
		return false;
	}

	//If there was an error in loading the font
	if( font == NULL )
	{
		return false;
	}

	//If everything loaded fine
	return true;
}

void clean_up()
{
	//Free the surfaces
	SDL_FreeSurface( pBackground );
	SDL_FreeSurface( pMessage );

	//Close the font
	TTF_CloseFont( font );

	//Quit SDL_ttf
	TTF_Quit();

	//Quit SDL
	SDL_Quit();
}

int main( int argc, char* args[] )
{
	//Quit flag
	bool quit = false;

	/* Keep track of the current frame. */
	int iFrame = 0;

	/* whether or not to cap the frame rate. */
	bool bCapFrameRate = true;

	/* The frame rate regulator. */
	Timer fps;

	/* Timer used to update the caption. */
	Timer update;

	//Initialize
	if( init() == false )
	{
		return 1;
	}

	//Load the files
	if( load_files() == false )
	{
		return 1;
	}

	//Generate the message surface
	pMessage = TTF_RenderText_Solid( font, "Testing Frame Rate", textColor );

	/* Start both the timers. */

	fps.start();

	update.start();

	//While the user hasn't quit
	while( quit == false )
	{
		//While there's an event to handle
		while( SDL_PollEvent( &event ) )
		{
			//If the user has Xed out the window
			if( event.type == SDL_QUIT )
			{
				//Quit the program
				quit = true;
			}
		}

		//Apply the background
		apply_surface( 0, 0, pBackground, pScreen );

		//Apply the message
		apply_surface( ( SCREEN_WIDTH - pMessage->w ) / 2, 250, pMessage, pScreen );

		/* Update the screen. */
		if( SDL_Flip( pScreen ) == -1 )
		{
			return 1;
		}

		/* Increment the frame counter. */
		iFrame++;

		/* If a second has passed since the caption was last reset. */
		if( update.get_Ticks() > 1000 )
		{
			/* The frame rate as a string. */
			std::stringstream caption;

			/* Calculate the average frames per second and create the string. */
			caption << "Average frames per second : " << iFrame / ( fps.get_Ticks() / 1000.0f );

			/* Reset the caption. */
			SDL_WM_SetCaption( caption.str().c_str(), nullptr );

			/* Restart the update timer. */
			update.start();
		}

	}

	//Clean up
	clean_up();

	return 0;
}
